package org.gaaidou.ptarmigan.server.io;

import org.gaaidou.ptarmigan.server.protocol.HttpParser;
import org.gaaidou.ptarmigan.server.protocol.HttpRequest;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Server implements Runnable {
    private final ServerSocket serverSocket;

    private HandlerRouter handlerRouter;

    private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 10, 30, TimeUnit.MINUTES, new ArrayBlockingQueue<>(200));

    public Server(int port, HandlerRouter handlerRouter) throws IOException {
        this(port);
        this.handlerRouter = handlerRouter;
    }

    public Server(int port) throws IOException {
        serverSocket = new ServerSocket(port);
    }

    public void setHandlerRouter(HandlerRouter router) {
        this.handlerRouter = router;
    }


    public void listen() throws InterruptedException {
        Thread thread = new Thread(this);
        thread.setDaemon(true);
        thread.start();
        thread.join();
        threadPoolExecutor.shutdown();
    }

    @Override
    public void run() {
        try {
            while (true) {
                Socket socket = serverSocket.accept();
                if (null == socket) {
                    break;
                }
                if (usingThreadPool()) {
                    threadPoolExecutor.execute(() -> handleSocket(socket));
                } else {
                    handleSocket(socket);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private void handleSocket(Socket socket) {
        try (InputStream is = socket.getInputStream(); OutputStream os = socket.getOutputStream()) {
            HttpRequest request = HttpParser.parseRequest(is);
            Handler route = handlerRouter.route(request.path());
            HttpParser.writeResponse(route.handle(request), os);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private boolean usingThreadPool() {
        return Boolean.TRUE.toString().equals(System.getProperty("use_thread_pool"));
    }
}
